#include <stdio.h>
#include <string.h>
#include "lexer.h"
#include "token.h"

const char *ILLEGAL = "ILLEGAL";
const char *MEOF = "MEOF";
const char *IDENT = "IDENT";
const char *INT = "INT";
// 运算符
const char *ASSIGN = "=";
const char *PLUS = "+";
const char *MINUS = "-";
const char *BANG = "!";
const char *ASTERISK = "*";
const char *SLASH = "/";

const char *LT = "<";
const char *GT = ">";
const char *EQ = "==";
const char *NOT_EQ = "!=";

const char *COMMA = ",";
const char *SEMICOLON = ";";
const char *LPAREN = "(";
const char *RPAREN = ")";
const char *LBRACE = "{";
const char *RBRACE = "}";

// 关键字
const char *FUNCTION = "FUNCTION";
const char *LET = "LET";
const char *TRUE = "TRUE";
const char *FALSE = "FALSE";
const char *IF = "IF";
const char *ELSE = "ELSE";
const char *RETURN = "RETURN";



struct s_lexer new_s_lexer(const char *str)
{
    struct s_lexer l = {.position = 0, .readPosition = 0};
    strcpy(l.input, str);
    s_lexer_read_char(&l);
    return l;
}

void s_lexer_read_char(struct s_lexer *l)
{
    if (l->readPosition >= strlen(l->input))
    {
        l->ch = 0;
    }
    else
    {
        l->ch = l->input[l->readPosition];
    }
    l->position = l->readPosition;
    l->readPosition += 1;
}

int is_letter(char ch) { return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ch == '_'; }

char *s_lexer_read_identifier(struct s_lexer *l, char *id)
{
    int i = 0;
    while (is_letter(l->ch))
    {
        id[i++] = l->ch;
        s_lexer_read_char(l);
    }
    id[i] = '\0';
    return id;
}

char *lookup_ident(const char *id, char *typ)
{
    if (strcmp(id, "let") == 0)
    {
        strcpy(typ, LET);
    }
    else if (strcmp(id, "fn") == 0)
    {
        strcpy(typ, FUNCTION);
    }
    else if (strcmp(id, "if") == 0)
    {
        strcpy(typ, IF);
    }
    else if (strcmp(id, "else") == 0)
    {
        strcpy(typ, ELSE);
    }
    else if (strcmp(id, "true") == 0)
    {
        strcpy(typ, TRUE);
    }
    else if (strcmp(id, "false") == 0)
    {
        strcpy(typ, FALSE);
    }
    else if (strcmp(id, "return") == 0)
    {
        strcpy(typ, RETURN);
    }
    else
    {
        strcpy(typ, IDENT);
    }
    return typ;
}

void skip_whitespace(struct s_lexer *l)
{
    while (l->ch == ' ' || l->ch == '\t' || l->ch == '\n' || l->ch == '\r')
    {
        s_lexer_read_char(l);
    }
}

int is_digit(char ch) { return '0' <= ch && ch <= '9'; }

char s_lexer_peek_char(const struct s_lexer *l)
{
    if (l->readPosition >= strlen(l->input))
    {
        return 0;
    }
    return l->input[l->readPosition];
}

char *s_lexer_read_number(struct s_lexer *l, char *num)
{
    int i = 0;
    while (is_digit(l->ch))
    {
        num[i++] = l->ch;
        s_lexer_read_char(l);
    }
    num[i] = '\0';
    return num;
}

struct s_token s_lexer_next_token(struct s_lexer *l)
{
    struct s_token t;
    skip_whitespace(l);
    switch (l->ch)
    {
    case '=':
        if (s_lexer_peek_char(l) == '=')
        {
            s_lexer_read_char(l);
            t = new_s_token(EQ, "==");
        }
        else
            t = new_s_token(ASSIGN, "=");
        break;
    case ';':
        t = new_s_token(SEMICOLON, ";");
        break;
    case '(':
        t = new_s_token(LPAREN, "(");
        break;
    case ')':
        t = new_s_token(RPAREN, ")");
        break;
    case ',':
        t = new_s_token(COMMA, ",");
        break;
    case '+':
        t = new_s_token(PLUS, "+");
        break;
    case '-':
        t = new_s_token(MINUS, "-");
        break;
    case '!':
        if (s_lexer_peek_char(l) == '=')
        {
            s_lexer_read_char(l);
            t = new_s_token(NOT_EQ, "!=");
        }
        else
            t = new_s_token(BANG, "!");
        break;
    case '/':
        t = new_s_token(SLASH, "/");
        break;
    case '*':
        t = new_s_token(ASTERISK, "*");
        break;
    case '<':
        t = new_s_token(LT, "<");
        break;
    case '>':
        t = new_s_token(GT, ">");
        break;
    case '{':
        t = new_s_token(LBRACE, "{");
        break;
    case '}':
        t = new_s_token(RBRACE, "}");
        break;
    case 0:
        t = new_s_token(MEOF, "");
        break;
    default:
        if (is_letter(l->ch))
        {
            char id[1024];
            s_lexer_read_identifier(l, id);
            s_token_set_literal(&t, id);
            char typ[16];
            s_token_set_type(&t, lookup_ident(id, typ));
            return t;
        }
        else if (is_digit(l->ch))
        {
            s_token_set_type(&t, INT);
            char num[32];
            s_lexer_read_number(l, num);
            s_token_set_literal(&t, num);
            return t;
        }
        else
        {
            t = new_s_token(ILLEGAL, "");
            t.literal[0] = l->ch;
            t.literal[1] = '\0';
            return t;
        }
    }
    s_lexer_read_char(l);
    return t;
}

